home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d18 / gsdb21.arc / GS_DBFLD.PAS < prev    next >
Pascal/Delphi Source File  |  1991-01-04  |  47KB  |  1,403 lines

  1. {                      dBase III Field Handler
  2.  
  3.        GS_DBFLD Copyright (c)  Richard F. Griffin
  4.  
  5.        15 November 1990
  6.  
  7.        102 Molded Stone Pl
  8.        Warner Robins, GA  31088
  9.  
  10.        -------------------------------------------------------------
  11.        This unit handles field processing for all dBase III file (.DBF)
  12.        operations.
  13.  
  14.                    SHAREWARE  -- COMMERCIAL USE RESTRICTED
  15.  
  16.  
  17.  
  18.        Changes:
  19.  
  20.  
  21.  
  22.  
  23. }
  24. {
  25.                            ┌──────────────────────┐
  26.                            │  INTERFACE SECTION:  │
  27.                            └──────────────────────┘
  28. }
  29. unit GS_dBFld;
  30.  
  31. interface
  32.  
  33. uses
  34.    CRT,
  35.    GS_Edit,
  36.    GS_FileH,
  37.    GS_Error,
  38.    GS_KeyI,
  39.    GS_Strng,
  40.    GS_Wind,
  41.    GS_dBase;
  42.  
  43. type
  44.    GS_dBFld_Objt   = object(GS_dBase_dB)
  45.       LastFldTyp   : char;            {Last FieldGet type field}
  46.       LastFldDec   : integer;         {Last FieldGet Decimals}
  47.       LastFldLth   : integer;         {Last FieldGet Length}
  48.       LastFldNam   : string[11];      {Last FieldGet Name}
  49.       LastFldNum   : integer;         {Last FieldGet Number}
  50.       EditOn       : boolean;         {Edit allowed}
  51.       RecChanged   : boolean;         {Flag for record changed}
  52.       Memo_Loc     : longint;         {Starting memo block for field}
  53.       Memo_Bloks   : integer;         {Number of blocks used for the field}
  54.       Memo_Store   : GS_Edit_Objt;    {Object to store/edit memos}
  55.       DeleteOnF9   : boolean;         {Flag to permit F9 to delete/undelete}
  56.  
  57.       Procedure Check_Func_Keys; virtual;
  58.       Function  Create(FName : string) : boolean;
  59.       function  DateGet(st : string) : string;
  60.       function  DateGetN(n : integer) : string;
  61.       Procedure DatePut(st, data : string);
  62.       Procedure DatePutN(n : integer; data : string);
  63.       Function  FieldAccept(st,Titl : string; x,y : integer) : string;
  64.       Procedure FieldDisplay(st,Titl : string; x,y : integer);
  65.       Function  FieldDisplayScreen : boolean;
  66.       Function  FieldGet(st : string) : string;
  67.       Function  FieldGetN(n : integer) : string;
  68.       Procedure FieldPut(st1, st2 : string);
  69.       Procedure FieldPutN(n : integer; st1 : string);
  70.       Function  FieldUpdateScreen : boolean;
  71.       Function  FieldAppendScreen(empty : boolean) : boolean;
  72.       Function  Formula(st : string) : string; virtual;
  73.       Function  HuntFieldName(st : string; var fs : integer) : boolean;
  74.       Procedure IndexTo(filname, formla : string);
  75.       Constructor Init(FName : string);
  76.       function  LogicGet(st : string) : boolean;
  77.       function  LogicGetN(n : integer) : boolean;
  78.       Procedure LogicPut(st : string; b : boolean);
  79.       Procedure LogicPutN(n : integer; b : boolean);
  80.       Procedure MemoEdit;
  81.       function  MemoGetLine(linenum : integer) : string;
  82.       procedure MemoGet(rpt : string);
  83.       Procedure MemoWidth(l : integer);
  84.       function  MemoLines : integer;
  85.       function  MemoPut : string;
  86.       function  NumberGet(st : string) : real;
  87.       function  NumberGetN(n : integer) : real;
  88.       Procedure NumberPut(st : string; r : real);
  89.       Procedure NumberPutN(n : integer; r : real);
  90.       Procedure Pack;
  91.       function  StringGet(st : string) : string;
  92.       function  StringGetN(n : integer) : string;
  93.       Procedure StringPut(st1, st2 : string);
  94.       Procedure StringPutN(n : integer; st1 : string);
  95.    end;
  96.  
  97. implementation
  98.  
  99. procedure GS_dBFld_Objt.Check_Func_Keys;
  100. begin
  101.    case ch of
  102.      Kbd_F9   : begin
  103.                    if DeleteOnF9 then
  104.                    begin
  105.                       if RecNumber < 0 then
  106.                       begin
  107.                          if DelFlag then CurRecord^[0] :=  32
  108.                             else CurRecord^[0] := 42;
  109.                          DelFlag := not DelFlag;
  110.                       end
  111.                          else if DelFlag then UnDelete else Delete;
  112.                       GS_KeyI_Ret := true;
  113.                       Ch := Kbd_Ret;
  114.                    end else GS_dBase_DB.Check_Func_Keys;
  115.                 end;
  116.      Kbd_F10  : begin
  117.                    GS_KeyI_Ret := true;
  118.                    Ch := Kbd_Ret;
  119.                 end;
  120.      else GS_dBase_DB.Check_Func_Keys;
  121.   end;
  122. end;
  123.  
  124.  
  125. function  GS_dBFld_Objt.DateGet(st : string) : string;
  126. var
  127.    t     : string;
  128. begin
  129.    t := FieldGet(st);
  130.    DateGet := StrDate(t);
  131. end;
  132.  
  133. function  GS_dBFld_Objt.DateGetN(n : integer) : string;
  134. var
  135.    data,
  136.    t     : string;
  137. begin
  138.    t := FieldGetN(n);
  139.    DateGetN := StrDate(t);
  140. end;
  141.  
  142. Procedure GS_dBFld_Objt.DatePut(st, data : string);
  143. var
  144.    f    : integer;
  145.    valu : string[2];
  146.    t    : string;
  147. begin
  148.    if not HuntFieldName(st,f) then
  149.    begin
  150.       ShowError(625,st);
  151.       exit;
  152.    end;
  153.    move(data[1], t[5], 2);
  154.    move(data[4], t[7], 2);
  155.    move(data[7], t[3], 2);
  156.    valu := '19';                {Use 19 for first two digits - this will}
  157.                                 {have to be changed in the year 2000}
  158.    move(valu[1], t[1], 2);      {Move the first two year digits to record}
  159.    t[0] := #8;
  160.    FieldPutN(f,t);
  161. end;
  162.  
  163. Procedure GS_dBFld_Objt.DatePutN(n : integer; data : string);
  164. var
  165.    valu : string[2];
  166.    t    : string;
  167. begin
  168.    if n > NumFields then
  169.    begin
  170.       ShowError(627,'Field number out of range');
  171.       exit;
  172.    end;
  173.    move(data[1], t[5], 2);
  174.    move(data[4], t[7], 2);
  175.    move(data[7], t[3], 2);
  176.    valu := '19';                {Use 19 for first two digits - this will}
  177.                                 {have to be changed in the year 2000}
  178.    move(valu[1], t[1], 2);      {Move the first two year digits to record}
  179.    t[0] := #8;
  180.    FieldPutN(n,t);
  181. end;
  182.  
  183. function  GS_dBFld_Objt.LogicGet(st : string) : boolean;
  184. begin
  185.    LogicGet := ValLogic(FieldGet(st));
  186. end;
  187.  
  188. function  GS_dBFld_Objt.LogicGetN(n : integer) : boolean;
  189. begin
  190.    LogicGetN := ValLogic(FieldGetN(n));
  191. end;
  192.  
  193. Procedure GS_dBFld_Objt.LogicPut(st : string; b : boolean);
  194. begin
  195.    FieldPut(st,StrLogic(b));
  196. end;
  197.  
  198. Procedure GS_dBFld_Objt.LogicPutN(n : integer; b : boolean);
  199. begin
  200.    FieldPutN(n,StrLogic(b));
  201. end;
  202.  
  203. function  GS_dBFld_Objt.NumberGet(st : string) : real;
  204. var
  205.    r : integer;
  206.    v : real;
  207.    s : string;
  208. begin
  209.    s := TrimR(FieldGet(st));
  210.    r := 0;
  211.    if s = '' then v := 0 else val(s,v,r);
  212.    if r <> 0 then
  213.    begin
  214.       ShowError(620,'Not a valid numeric field in NumberGet'+s);
  215.       v := 0;
  216.    end;
  217.    NumberGet := v;
  218. end;
  219.  
  220. function  GS_dBFld_Objt.NumberGetN(n : integer) : real;
  221. var
  222.    r : integer;
  223.    v : real;
  224.    s : string;
  225. begin
  226.    s := TrimR(FieldGetN(n));
  227.    r := 0;
  228.    if s = '' then v := 0 else val(s,v,r);
  229.    if r <> 0 then
  230.    begin
  231.       ShowError(620,'Not a valid numeric field in NumberGetN - '+s);
  232.       v := 0;
  233.    end;
  234.    NumberGetN := v;
  235. end;
  236.  
  237. Procedure GS_dBFld_Objt.NumberPut(st : string; r : real);
  238. var
  239.    f : integer;
  240.    s : string;
  241. begin
  242.    if not HuntFieldName(st,f) then
  243.    begin
  244.       ShowError(625,st);
  245.       exit;
  246.    end;
  247.    Str(r:LastFldLth:LastFldDec,s);
  248.    FieldPutN(f,s);
  249. end;
  250.  
  251. Procedure GS_dBFld_Objt.NumberPutN(n : integer; r : real);
  252. var
  253.    s : string;
  254. begin
  255.    if n > NumFields then
  256.    begin
  257.       ShowError(627,'Field number out of range');
  258.       exit;
  259.    end;
  260.    Str(r:Fields^[n].FieldLen:Fields^[n].FieldDec,s);
  261.    FieldPutN(n,s);
  262. end;
  263.  
  264. function  GS_dBFld_Objt.StringGet(st : string) : string;
  265. begin
  266.    StringGet := TrimR(FieldGet(st));
  267. end;
  268.  
  269. function  GS_dBFld_Objt.StringGetN(n : integer) : string;
  270. begin
  271.    StringGetN := TrimR(FieldGetN(n));
  272. end;
  273.  
  274. Procedure GS_dBFld_Objt.StringPut(st1,st2 : string);
  275. begin
  276.    FieldPut(st1,st2);
  277. end;
  278.  
  279. Procedure GS_dBFld_Objt.StringPutN(n : integer; st1 : string);
  280. begin
  281.    FieldPutN(n,st1);
  282. end;
  283.  
  284. function GS_dBFld_Objt.HuntFieldName(st : string; var fs : integer) : boolean;
  285. var
  286.    FSt : string;
  287.    mtch : boolean;
  288. begin
  289.    FSt := AllCaps(st);             {Capitalize the workstring}
  290.    FSt := TrimR(FSt);              {Remove trailing spaces}
  291.    fs := 1;                        {Initialize field count}
  292.    mtch := false;                  {Set match found to false}
  293.    while (not mtch) and (fs <= NumFields) DO
  294.       if FieldsN^[fs] = FSt then mtch := true else inc(fs);
  295.    if mtch then
  296.    begin
  297.       LastFldTyp := Fields^[fs].FieldType;
  298.       LastFldDec := Fields^[fs].FieldDec;
  299.       LastFldLth := Fields^[fs].FieldLen;
  300.    end;
  301.    HuntFieldName := mtch;
  302. end;
  303.  
  304. Function GS_dBFld_Objt.Create(FName : string) : boolean;
  305. begin
  306.    if GS_dBase_DB.Create(FName) then
  307.    begin
  308.       Init(FName);
  309.       Create := true;
  310.    end else Create := false;
  311. end;
  312.  
  313. Procedure GS_dBFld_Objt.Pack;
  314. const
  315.    EOFMark : Byte = $1A;
  316. var
  317.    df   : file;                       {Local file variable for memo work file}
  318.    mbuf : array[0..GS_dBase_MaxMemoRec] of byte;
  319.    rsl  : word;
  320.    i, j : longint;                    {Local variables   }
  321.    mcnt,
  322.    tcnt : longint;
  323.    done : boolean;
  324.    rl   : real;
  325.    FNam : string[64];
  326.  
  327.    procedure UpdateMemo;
  328.    var
  329.       fp : integer;
  330.    begin
  331.       for fp := 1 to NumFields do
  332.       begin
  333.          if Fields^[fp].FieldType = 'M' then
  334.          begin
  335.             Memo_Loc := Trunc(NumberGetN(fp));
  336.             Memo_Bloks := 0;          {Initialize blocks read}
  337.             if (Memo_Loc <> 0) then
  338.             begin
  339.                tcnt := GS_FileSize(df);
  340.                rl := tcnt;
  341.                NumberPutN(fp,rl);
  342.                 done := false;         {Reset done flag to false}
  343.                while (not done) do    {loop until done (EOF mark)}
  344.                begin
  345.                   GS_FileRead(mFile, Memo_Loc+Memo_Bloks, mbuf, 1, rsl);
  346.                   inc(Memo_Bloks);
  347.                   mCnt := 0;          {Counter into disk read buffer}
  348.                   while (mCnt < GS_dBase_MaxMemoRec) and (done = false) do
  349.                   begin
  350.                      if mbuf[mcnt] = $1A then done := true;
  351.                      inc (mcnt);
  352.                   end;
  353.                   if not done then GS_FileWrite(df,-1,mbuf,1, rsl);
  354.                end;
  355.                FillChar(mbuf[mcnt],GS_dBase_MaxMemoRec - mcnt,#0);
  356.                GS_FileWrite(df,-1,mbuf,1, rsl);
  357.                                       {Write the last block to the .DBT}
  358.             end;
  359.          end;
  360.       end;
  361.    end;
  362.  
  363. begin      {Pack}
  364.    i := 1;
  365.    while dbfNdxTbl[i] <> nil do
  366.    begin
  367.       dbfNdxTbl[i]^.Ndx_Close;
  368.       Dispose(dbfNdxTbl[i]);
  369.       dbfNdxTbl[i] := nil;
  370.       inc(i);
  371.    end;
  372.    dbfNdxActv := false;               {Set index active flag to false}
  373.    j := 0;
  374.    if WithMemo then
  375.    begin
  376.       GS_FileAssign(df,'DB3$$$.D$$',2048);
  377.       GS_FileRewrite(df,GS_dBase_MaxMemoRec);
  378.       FillChar(mbuf,GS_dBase_MaxMemoRec,#0);
  379.       mbuf[0] := 1;
  380.       GS_FileWrite(df,0,mbuf,1,rsl);
  381.    end;
  382.    for i := 1 to NumRecs do           {Read .DBF sequentially}
  383.    begin
  384.       GetRec(i);
  385.       if not DelFlag then             {Write to work file if not deleted}
  386.       begin
  387.          inc(j);                      {Increment record count for packed file }
  388.          if WithMemo then UpdateMemo;
  389.          PutRec(j);
  390.       end;
  391.    end;
  392.    if i > j then                      {If records were deleted then...}
  393.    begin
  394.       NumRecs := j;                   {Store new record count in objectname}
  395.       GS_FileWrite(dfile, HeadLen+(j*RecLen)+1, EOFMark, 1, rsl);
  396.                                       {Write End of File byte at file end}
  397.       GS_FileTruncate(dfile,HeadLen+(j*RecLen)+1);
  398.                                       {Set new file size for dBase file};
  399.    end;
  400.    if WithMemo then
  401.    begin
  402.       tcnt := GS_FileSize(df);
  403.       FillChar(mbuf,GS_dBase_MaxMemoRec,#0);
  404.       Move(tcnt,mbuf[0],4);
  405.       GS_FileWrite(df,0,mbuf,1, rsl);
  406.                                       {Write the block to the .DBT.  It will}
  407.                                       {point to the next available block};
  408.       FNam := FileName;
  409.       FNam[length(FNam)] := 'T';
  410.       GS_FileClose(mFile);
  411.       GS_FileClose(df);
  412.       GS_FileErase(mFile);            {Erase original file}
  413.       GS_FileRename(df, FNam);        {Rename work file to original file name}
  414.       GS_FileAssign(mFile, FNam, 2048); {Set file type to new file}
  415.       GS_FileReset(mFile, GS_dBase_MaxMemoRec);
  416.    end;
  417. END;                        { Pack }
  418.  
  419. Function GS_dBFld_Objt.FieldAccept(st,Titl : string; x,y : integer) : string;
  420. var
  421.    txtatrb,
  422.    i,
  423.    v         :  integer;              {Counter variables}
  424.    t         :  string[255];          {Work string to hold default (old) value}
  425.    f         : string[2];
  426.  
  427.    Procedure AcceptC;
  428.    var
  429.       r_c : string;
  430.    begin
  431.       GS_Wind_SetIVMode;
  432.       if EditOn then        {If edit permitted, then go edit string}
  433.       begin
  434.          r_c := t;
  435.          t := EditString(t, v, y, LastFldLth);
  436.          if t <> r_c then RecChanged := true;
  437.       end
  438.       else
  439.       begin
  440.          gotoxy(v,y);       {Go to start of field screen position}
  441.          write(t,'':LastFldLth-length(t));
  442.                             {Rewrite the string on screen inverted}
  443.          WaitForKey;
  444.       end;
  445.       GS_Wind_SetNmMode;
  446.       gotoxy(v,y);          {Go to start of field screen position}
  447.       write(t,'':LastFldLth-length(t));
  448.                             {Rewrite the string on screen in the original color}
  449.    end;
  450.  
  451.    Procedure AcceptD;
  452.    var
  453.       data   : string[10];
  454.       valu,
  455.       yy,
  456.       mm,
  457.       dd     : string[2];
  458.       mmn,
  459.       ddn,
  460.       yyn,
  461.       rsl    : integer;
  462.       cc     : char;
  463.       okDate : boolean;
  464.    begin
  465.       t := StrDate(t);
  466.       okDate := false;
  467.       repeat
  468.          AcceptC;
  469.          if not EditOn then exit;
  470.          if TrimR(t) = '  /  /' then exit;
  471.          data := t;
  472.          cc := t[3];
  473.          if cc in ['0'..'9'] then
  474.          begin
  475.             mm := copy(data,5,2);
  476.             dd := copy(data,7,2);
  477.             yy := copy(data,3,2);
  478.          end
  479.          else
  480.          begin
  481.             mm := copy(data,1,2);
  482.             dd := copy(data,4,2);
  483.             yy := copy(data,7,2);
  484.          end;
  485.          val(mm,mmn,rsl);
  486.          if rsl = 0 then
  487.          begin
  488.             val(dd,ddn,rsl);
  489.             if rsl = 0 then
  490.             begin
  491.                val(yy,yyn,rsl);
  492.                if rsl = 0 then
  493.                begin
  494.                   if mmn in [1..12] then
  495.                      if ddn in [1..31] then
  496.                         okDate := true;
  497.                end;
  498.             end;
  499.          end;
  500.          if not okDate then SoundBell(BeepTime,BeepFreq);
  501.       until okDate;
  502.       if cc in ['0'..'9'] then begin end
  503.       else
  504.       begin
  505.          move(data[1], t[5], 2);
  506.          move(data[4], t[7], 2);
  507.          move(data[7], t[3], 2);
  508.          valu := '19';                {Use 19 for first two digits - this will}
  509.                                       {have to be changed in the year 2000}
  510.          move(valu[1], t[1], 2);      {Move the first two year digits to record}
  511.          t[0] := #8;
  512.       end;
  513.    end;
  514.  
  515.    Procedure AcceptL;
  516.    var
  517.       data : string[1];
  518.    begin
  519. {
  520.                     ┌─────────────────────────────────────┐
  521.                     │  Accept keyboard entry.  Loop until │
  522.                     │  value is T,t,Y,y,F,f,N,n.          │
  523.                     └─────────────────────────────────────┘
  524. }
  525.       repeat
  526.          if t = '' then t := 'F';
  527.          AcceptC;
  528.          if not EditOn then exit;
  529.          if t[1] in ['T','t','Y','y','F','f','N','n'] then
  530.          begin end else SoundBell(BeepTime,BeepFreq);
  531.       until t[1] in ['T','t','Y','y','F','f','N','n'];
  532.       if t[1] in ['T','t','Y','y'] then t[1] := 'T' else t[1] := 'F';
  533.    end;
  534.  
  535.    procedure AcceptM;
  536.    var
  537.       ans       :  string[10];        {Work string to hold edit value}
  538.       r_c       :  string[10];        {Work string for memo block number}
  539.    begin
  540.       GS_Wind_SetIvMode;
  541.       ans := 'N';                     {Initialize ans to false}
  542.       if EditOn then write('  Edit ? ') else write('  View ? ');
  543.       repeat
  544.          ans := EditString(ans,v+9,y,1);
  545.                                       {Go edit string t for 1 character}
  546.                                       {at cursor position v,y}
  547.          if ans[1] in ['T','t','Y','y','F','f','N','n'] then
  548.             begin end else SoundBell(BeepTime,BeepFreq);
  549.       until ans[1] in ['T','t','Y','y','F','f','N','n'];
  550.       GS_Wind_SetNmMode;              {Restore original text attribute}
  551.       gotoxy(v,y);                    {Now reset to 'memo' for field name}
  552.       write('---memo---');
  553.       if ans[1] in ['T','t','Y','y'] then
  554.       begin
  555.          r_c := t;
  556.          MemoGet(t);
  557.          If EditOn then Memo_Store.Edit else Memo_Store.View;
  558.          if (EditOn) and (GS_KeyI_Esc) then
  559.          begin
  560.             GS_KeyI_Esc := false;     {Reset Escape flag so its not used}
  561.                                       {elsewhere}
  562.             GS_KeyI_Chr := ' ';
  563.             MemoGet(t);
  564.          end
  565.          else
  566.          begin
  567.             GS_KeyI_Chr := ' ';       {Clear character last entered}
  568.             if EditOn then t := MemoPut;
  569.             if t <> r_c then RecChanged := true;
  570.          end;
  571.       end;
  572.    end;
  573.  
  574.    Procedure AcceptN;
  575.    var
  576.       data : string;
  577.       i   : integer;
  578.       r   : real;
  579.    begin
  580. {
  581.                     ┌─────────────────────────────────────┐
  582.                     │  Accept keyboard entry.  Loop until │
  583.                     │  value is Numeric.                  │
  584.                     └─────────────────────────────────────┘
  585. }
  586.       repeat
  587.          if t = '' then Str(0.0:LastFldLth:LastFldDec,t);
  588.          AcceptC;
  589.          if not EditOn then exit;
  590.          val(t, r, i);
  591.          if i = 0 then
  592.          begin
  593.             Str(r:LastFldLth:LastFldDec,t);
  594.             if length(t) > LastFldLth then i := 999;
  595.          end;
  596.          if i <> 0 then
  597.          begin
  598.             SoundBell(BeepTime,BeepFreq);
  599.             t := '';
  600.          end;
  601.       until i = 0;                    {i will be 0 when data is a valid number}
  602.       gotoxy(v,y);          {Go to start of field screen position}
  603.       write(t,'':LastFldLth-length(t));
  604.                             {Rewrite the string on screen in the original color}
  605.    end;
  606.  
  607. begin
  608.    GotoXY(x,y);                       {Go to position on screen}
  609.    write(Titl);                       {Write the title of field}
  610.    v := WhereX;                       {Save the position after writing title}
  611.    t := TrimR(FieldGet(st));          {Get the field in the work string}
  612.    case LastFldTyp of
  613.       'C'  : begin
  614.                 AcceptC;
  615.                 FieldAccept := t;     {Return the string to calling routine}
  616.              end;
  617.       'D'  : begin
  618.                 AcceptD;
  619.                 FieldAccept := t;
  620.              end;
  621.       'L'  : begin
  622.                 AcceptL;
  623.                 FieldAccept := t;
  624.              end;
  625.       'M'  : begin
  626.                 AcceptM;
  627.                 FieldAccept := t;
  628.              end;
  629.       'N'  : begin
  630.                 AcceptN;
  631.                 FieldAccept := t;
  632.              end;
  633.    end;
  634. end;
  635.  
  636. Procedure GS_dBFld_Objt.FieldDisplay(st,Titl : string; x,y : integer);
  637. var
  638.    i,
  639.    v         :  integer;              {Counter variables}
  640.    t         :  string[255];          {Work string to hold default (old) value}
  641.    data      :  string[10];
  642. begin
  643.    GotoXY(x,y);                       {Go to position on screen}
  644.    write(Titl);                       {Write the title of field}
  645.    v := WhereX;                       {Save the position after writing title}
  646.    t := TrimR(FieldGet(st));          {Get the field in the work string}
  647.  
  648.    case LastFldTyp of
  649.       'C',
  650.       'L'  : begin
  651.                 gotoxy(v,y);          {Go to start of field screen position}
  652.                 write(t,'':LastFldLth-length(t));
  653.                                       {Write the string on screen }
  654.              end;
  655.       'D'  : begin
  656.                 t := StrDate(t);
  657.                 write(t);
  658.              end;
  659.       'N'  : begin
  660.                 if t = '' then t := '0';
  661.                 gotoxy(v,y);          {Go to start of field screen position}
  662.                 write(t:LastFldLth);
  663.              end;
  664.       'M'  : begin
  665.                 gotoxy(v,y);          {Go to start of field screen position}
  666.                 write('---memo---');  {Write the '---memo--- on screen }
  667.              end;
  668.    end;
  669. end;
  670.  
  671. Function GS_dBFld_Objt.FieldDisplayScreen : boolean;
  672. var
  673.    f,
  674.    h     : boolean;
  675. begin
  676.    h := EditOn;
  677.    EditOn := false;
  678.    f := FieldUpdateScreen;
  679.    EditOn := h;
  680.    FieldDisplayScreen := f;
  681. end;
  682.  
  683. function GS_dBFld_Objt.FieldGetN(n : integer) : String;
  684. var
  685.    os,
  686.    fs  : longint;
  687.    i,
  688.    k   : integer;
  689.    FSt,
  690.    WSt : string[255];
  691.    NSt : string[10];
  692. begin
  693.    fs := n;                        {Initialize field count}
  694.    if (fs <= NumFields) then
  695.    BEGIN
  696.       os := 1;
  697.       WITH Fields^[fs] DO
  698.       BEGIN
  699.          CnvAscToStr(FieldName,FSt,11);
  700.          FSt := TrimR(FSt);           {Remove trailing spaces}
  701.          move(CurRecord^[FieldAddress], WSt[1], FieldLen);
  702.          WSt[0] := char(FieldLen);    {Set string length to field length}
  703.          FieldGetN := WSt;
  704.          LastFldTyp := FieldType;
  705.          LastFldDec := FieldDec;
  706.          LastFldLth := FieldLen;
  707.          LastFldNum := fs;
  708.          LastFldNam := FSt;
  709.       end;
  710.    end else
  711.    begin
  712.       str(n,NSt);
  713.       ShowError(603,NSt);
  714.       FieldGetN := '';
  715.       LastFldTyp := ' ';
  716.       LastFldDec := 0;
  717.       LastFldLth := 0;
  718.       LastFldNum := 0;
  719.       LastFldNam := '';
  720.    end;
  721. end;
  722.  
  723. function GS_dBFld_Objt.FieldGet(st : string) : String;
  724. var
  725.    fs : integer;
  726. begin
  727.    if HuntFieldName(st,fs) then FieldGet := FieldGetN(fs)
  728.    else
  729.    begin
  730.       ShowError(602,st);
  731.       FieldGet := '';
  732.       LastFldTyp := ' ';
  733.       LastFldDec := 0;
  734.       LastFldLth := 0;
  735.       LastFldNum := 0;
  736.       LastFldNam := '';
  737.    end;
  738. end;
  739.  
  740.  
  741. Procedure GS_dBFld_Objt.FieldPutN(n : integer; st1 : string);
  742. var
  743.    os,
  744.    fs  : longint;
  745.    i,
  746.    k   : integer;
  747.    FSt,
  748.    WSt : string[255];
  749.    NSt : string[10];
  750. begin
  751.    fs := n;                        {Initialize field count}
  752.    if (fs <= NumFields) then
  753.    BEGIN
  754.       WITH Fields^[fs] DO
  755.       BEGIN
  756.          move(FieldName,FSt[1],11);
  757.          FSt[0] := #11;
  758.          FSt[0] := char(pred(pos(#0,FSt)));
  759.          FSt := TrimR(FSt);        {Remove trailing spaces}
  760.          FillChar(CurRecord^[FieldAddress], FieldLen, ' ');
  761.          k := length(st1);         {Get length of input string}
  762.          if k > FieldLen then k := FieldLen;
  763.          Move(st1[1], CurRecord^[FieldAddress], k);
  764.          LastFldTyp := FieldType;
  765.          LastFldDec := FieldDec;
  766.          LastFldLth := FieldLen;
  767.          LastFldNum := fs;
  768.          LastFldNam := FSt;
  769.       end;
  770.    end else
  771.    begin
  772.       str(n,NSt);
  773.       ShowError(605,NSt);
  774.       LastFldTyp := ' ';
  775.       LastFldDec := 0;
  776.       LastFldLth := 0;
  777.       LastFldNum := 0;
  778.       LastFldNam := '';
  779.    end;
  780. end;
  781.  
  782. Procedure GS_dBFld_Objt.FieldPut(st1, st2 : string);
  783. var
  784.    fs : integer;
  785. begin
  786.    if HuntFieldName(st1,fs) then FieldPutN(fs,st2)
  787.    else
  788.    begin
  789.       ShowError(604,st1);
  790.       LastFldTyp := ' ';
  791.       LastFldDec := 0;
  792.       LastFldLth := 0;
  793.       LastFldNum := 0;
  794.       LastFldNam := '';
  795.    end;
  796. end;
  797.  
  798. Function GS_dBFld_Objt.FieldUpdateScreen : boolean;
  799. var
  800.    b,
  801.    i,
  802.    v,
  803.    x,
  804.    y,
  805.    ll    : integer;
  806.    st,
  807.    s     : string[12];
  808.    t     : string;
  809.    activlin,
  810.    activfld : integer;
  811.  
  812.  
  813.    Procedure UpdatePage;
  814.    var
  815.       validcmd : boolean;
  816.    begin
  817.       validcmd := false;
  818.       if activfld < b then activfld := b;
  819.       if activfld >= b+v then activfld := pred(b+v);
  820.       activlin := succ(activfld - b);
  821.       if (activlin < 1) or (activlin > v) then activlin := 1;
  822.       repeat
  823.          t := FieldAccept(FieldsN^[activfld],'',13,activlin);
  824.          if (EditOn) and (not GS_KeyI_Esc) then FieldPutN(activfld,t);
  825.          if (not GS_KeyI_Fuc) and (GS_KeyI_Chr >= #32) then
  826.             GS_KeyI_Chr := Kbd_Ret;
  827.  
  828.             case GS_KeyI_Chr of
  829.                Kbd_F9 :   begin
  830.                              gotoxy(3,ll);
  831.                              GS_Wind_SetIvMode;
  832.                              if DelFlag then write('Deleted')
  833.                                 else write('':8);
  834.                              GS_Wind_SetNmMode;
  835.                           end;
  836.                Kbd_PgUp : begin
  837.                              if activfld = b then
  838.                              begin
  839.                                 b := b-v;
  840.                                 if b < 1 then b := 1;
  841.                                 validcmd := true;
  842.                              end
  843.                              else activfld := b;
  844.                           end;
  845.                Kbd_PgDn : begin
  846.                              if activfld = pred(b+v) then
  847.                              begin
  848.                                 b := b+v;
  849.                                 if b > NumFields-v then b := succ(NumFields-v);
  850.                                 if b < 1 then b := 1;
  851.                                 validcmd := true;
  852.                              end
  853.                              else activfld := pred(b+v);
  854.                           end;
  855.                Kbd_UpAr : begin
  856.                              dec(activfld);
  857.                              if activfld < b then
  858.                              begin
  859.                                 dec(b);
  860.                                 if b < 1 then b := 1;
  861.                                 validcmd := true;
  862.                              end;
  863.                           end;
  864.                Kbd_RtAr,
  865.                Kbd_Tab,
  866.                Kbd_Ret,
  867.                Kbd_DnAr : begin
  868.                              inc(activfld);
  869.                              if activfld > pred(b+v) then
  870.                              begin
  871.                                 if activfld > NumFields then
  872.                                    activfld := NumFields
  873.                                 else
  874.                                 begin
  875.                                    inc(b);
  876.                                    if b > NumFields then
  877.                                       b := succ(NumFields-v);
  878.                                    validcmd := true;
  879.                                 end;
  880.                              end;
  881.                           end;
  882.                Kbd_Esc,
  883.                Kbd_F10  : validcmd := true;
  884.             end;
  885.  
  886.          if activfld < b then activfld := b;
  887.          if activfld >= b+v then activfld := pred(b+v);
  888.          activlin := succ(activfld - b);
  889.          if (activlin < 1) or (activlin > v) then activlin := 1;
  890.       until validcmd;
  891.    end;
  892.  
  893. begin
  894.    ClrScr;
  895.    DeleteOnF9 := true;
  896.    RecChanged := false;
  897.    b := 1;
  898.    activfld := b;
  899.    ll := succ(hi(WindMax)-hi(WindMin));
  900.    v := pred(ll);
  901.    GS_Wind_SetIvMode;
  902.    gotoxy(2,ll);
  903.    write('':pred(lo(WindMax)-lo(WindMin)));
  904.    if EditOn then
  905.    begin
  906.       if RecNumber < 0 then           {If Append, do the following}
  907.       begin
  908.          gotoxy(12,ll);
  909.          write('Append ');
  910.          write('EOF/',NumRecs);
  911.       end
  912.       else
  913.       begin                           {If Update do the following}
  914.          gotoxy(12,ll);
  915.          write('Update ');
  916.          write(RecNumber,'/',NumRecs);
  917.       end;
  918.    end else
  919.    begin                              {If Display then do this}
  920.       gotoxy(12,ll);
  921.       write('Display ');
  922.       write(RecNumber,'/',NumRecs);
  923.    end;
  924.    if DelFlag then
  925.    begin
  926.       gotoxy(3,ll);
  927.       write('Deleted');
  928.    end;
  929.    GS_Wind_SetNmMode;
  930.    if NumFields < v then v := NumFields;
  931.    x := 1;
  932.    y := 1;
  933.    Ch := ' ';
  934.    repeat
  935.       for i := b to pred(b+v) do
  936.       begin
  937.          s := FieldsN^[i];
  938.          FillChar(st[1],12,' ');
  939.          move(s[1],st[11-length(s)],length(s));
  940.          st[11] := ':';
  941.          st[0] := #12;
  942.          FieldDisplay(s,st,x,y);
  943.          case LastFldTyp of
  944.            'M' : begin
  945.                     gotoxy(x+12,y);
  946.                     write('---memo---');
  947.                     if RecNumber < 0 then FieldPutN(LastFldNum,' ');
  948.                                       {If Append, make sure memo field is not}
  949.                                       {pointing to a memo block              }
  950.                  end;
  951.          end;
  952.          ClrEol;
  953.          inc(y);
  954.       end;
  955.       UpdatePage;
  956.       y := 1;
  957.    until (GS_KeyI_Chr in [Kbd_Esc,Kbd_F10]) or
  958.          ((GS_KeyI_Chr = Kbd_PgUp) and (activfld = 1)) or
  959.          ((GS_KeyI_Chr = Kbd_PgDn) and (activfld = NumFields));
  960.    DeleteOnF9 := false;
  961.    if GS_KeyI_Chr in [Kbd_F10, Kbd_PgUp, Kbd_PgDn] then
  962.       FieldUpdateScreen := true
  963.    else FieldUpdateScreen := false;
  964. end;
  965.  
  966. Function GS_dBFld_Objt.FieldAppendScreen(empty : boolean) : boolean;
  967. begin
  968.    if empty then Blank;
  969.    CurRecord^[0] := 32;                   {Ensure delete flag is off}
  970.    DelFlag := false;
  971.    RecNumber := -1;
  972.    FieldAppendScreen := FieldUpdateScreen;
  973. end;
  974.  
  975. Function GS_dBFld_Objt.Formula(st : string) : string;
  976. var
  977.    FldVal,
  978.    FldWrk : string;
  979.    FldPos : integer;
  980.  
  981.    function HuntField(fldst : string) : String;
  982.    var
  983.       fs   : integer;
  984.       ss   : string;
  985.       FSt  : string;
  986.       mtch : boolean;
  987.    begin
  988.       FSt := AllCaps(fldst);          {Capitalize the workstring}
  989.       FSt := TrimR(FSt);              {Remove trailing spaces}
  990.       fs := 1;                        {Initialize field count}
  991.       mtch := false;                  {Set match found to false}
  992.       while (not mtch) and (fs <= NumFields) DO
  993.          if FieldsN^[fs] = FSt then mtch := true else inc(fs);
  994.       if mtch then
  995.       begin
  996.          WITH Fields^[fs] DO
  997.          BEGIN
  998.             move(CurRecord^[FieldAddress], FSt[1], FieldLen);
  999.             FSt[0] := char(FieldLen);    {Set string length to field length}
  1000.             HuntField := FSt;
  1001.          end;
  1002.       end
  1003.       else
  1004.       begin
  1005.          ss := TrimL(fldst);
  1006.          if ss = '' then
  1007.          begin
  1008.             HuntField := '';
  1009.             exit;
  1010.          end;
  1011.          if ss[1] = '"' then
  1012.          begin
  1013.             ss := TrimR(ss);
  1014.             system.delete(ss,1,1);
  1015.             if ss[length(ss)] = '"' then ss[0] := chr(pred(length(ss)));
  1016.             HuntField := ss;
  1017.             exit;
  1018.          end;
  1019.          ShowError(601,st+' ('+fldst+')');
  1020.          HuntField  := '';
  1021.       end;
  1022.    end;
  1023.  
  1024. begin
  1025.    FldVal := '';                      {Initialize the return string value}
  1026.    FldWrk := st;                      {Move the input string to a work field}
  1027.    while FldWrk <> '' do              {Repeat while there is still something}
  1028.                                       {in the work field.}
  1029.    begin
  1030.       FldPos := pos('+', FldWrk);     {Search for a '+' delimiter}
  1031.       if FldPos = 0 then FldPos := length(FldWrk)+1;
  1032.                                       {If no '+' then simulate for this pass}
  1033.                                       {by setting position to one beyond the}
  1034.                                       {end of the target field string.}
  1035.       FldVal := FldVal + HuntField(SubStr(FldWrk,1,FldPos-1));
  1036.                                       {Go find the field using the substring}
  1037.                                       {from the string's beginning to one }
  1038.                                       {position before the '+' character.}
  1039.       system.delete(FldWrk,1,FldPos); {Delete the string up through the '+'};
  1040.       FldWrk := TrimL(FldWrk);        {Remove leading spaces}
  1041.    end;
  1042.    Formula := FldVal;                 {Return value to calling routine}
  1043. end;
  1044.  
  1045. Procedure GS_dBFld_Objt.IndexTo(filname, formla : string);
  1046. var
  1047.    i,
  1048.    j,
  1049.    fl : integer;                      {Local working variable}
  1050.    ft : char;
  1051.  
  1052.  
  1053. {
  1054.              ┌──────────────────────────────────────────────────┐
  1055.              │  This routine will accumulate the field length   │
  1056.              │  of all fields passes in the calling argument.   │
  1057.              │  This is needed to pass the formula length to    │
  1058.              │  create the index header.                        │
  1059.              └──────────────────────────────────────────────────┘
  1060. }
  1061.  
  1062.  
  1063.    procedure AccumField;
  1064.    var
  1065.       FldWrk : string;
  1066.       FldLoc,
  1067.       FldPos : integer;
  1068.    begin
  1069.       ft := '*';                      {Set field type to new '*'}
  1070.       fl := 0;                        {initialize field length}
  1071.       FldWrk := TrimR(formla);        {Remove trailing spaces from argument}
  1072.       while FldWrk <> '' do           {Repeat while there is still something}
  1073.                                       {in the work field.}
  1074.       begin
  1075.          FldPos := pos('+', FldWrk);  {Search for a '+' delimiter}
  1076.          if FldPos = 0 then FldPos := length(FldWrk)+1;
  1077.                                       {If no '+' then simulate for this pass}
  1078.                                       {by setting position to one beyond the}
  1079.                                       {end of the target field string.}
  1080.  
  1081.                                       {Go find the field using the substring}
  1082.                                       {from the string's beginning to one }
  1083.                                       {position before the '+' character.}
  1084.         if not HuntFieldName(SubStr(FldWrk,1,FldPos-1),FldLoc) then
  1085.          begin
  1086.             fl := 0;
  1087.             exit;
  1088.          end;
  1089.          if ft = '*' then ft := LastFldTyp
  1090.             else ft := 'C';           {Set type to C if more than one field}
  1091.                                       {Else save this field's type         }
  1092.          fl := fl + Fields^[FldLoc].FieldLen;
  1093.                                       {If a valid field, then add the field}
  1094.                                       {length to the total field length value.}
  1095.          system.delete(FldWrk,1,FldPos);
  1096.                                       {Delete the string up through the '+'};
  1097.          FldWrk := TrimL(FldWrk);     {Remove leading spaces}
  1098.       end;
  1099.    end;
  1100.  
  1101. {
  1102.              ┌──────────────────────────────────────────────────┐
  1103.              │  Main routine.  This takes and analyzes the      │
  1104.              │  argument to build an index file.  It does the   │
  1105.              │  following:                                      │
  1106.              │  1.  Reset current index files.                  │
  1107.              │  2.  Get the total new formula field length.     │
  1108.              │  3.  Create an index file.                       │
  1109.              │  4.  Build the index by reading all dbase        │
  1110.              │      records and updating the index file.        │
  1111.              └──────────────────────────────────────────────────┘
  1112. }
  1113.  
  1114. begin
  1115.    i := 1;
  1116.    while dbfNdxTbl[i] <> nil do
  1117.    begin
  1118.       dbfNdxTbl[i]^.Ndx_Close;
  1119.       Dispose(dbfNdxTbl[i]);
  1120.       dbfNdxTbl[i] := nil;
  1121.       inc(i);
  1122.    end;
  1123.    dbfNdxActv := false;               {Set index active flag to false}
  1124.    if formla <> '' then
  1125.    begin
  1126.       AccumField;                     {Get field length of the formula}
  1127.       if fl = 0 then
  1128.       begin
  1129.          ShowError(601,formla);       {Display Error if formula is bad}
  1130.          exit;                        {Exit if formula is no good}
  1131.       end;
  1132.       New(dbfNdxTbl[1]);              {Create a new index object}
  1133.       dbfNdxTbl[1]^.Ndx_Make(filname, formla, fl, ft);
  1134.                                       {Go create an index}
  1135.       Open;
  1136.       GetRec(Top_Record);             {Read all dBase file records}
  1137.       while not File_EOF do
  1138.       begin
  1139.          dbfNdxTbl[1]^.KeyUpdate(Formula(formla),RecNumber,-1);
  1140.                                       {Insert record in the index}
  1141.          GetRec(Next_Record);
  1142.       end;
  1143. {      dbfNdxTbl[1]^.KeyList('PRN');}
  1144.       dbfNdxActv := true;             {Set index active flag true if index }
  1145.       GetRec(Top_Record);             {Reset to top record}
  1146.    end;
  1147. end;
  1148.  
  1149. constructor GS_dBFld_Objt.Init(FName : string);
  1150. begin
  1151.    EditOn := true;
  1152.    GS_dBase_DB.Init(FName);
  1153.    Memo_Store.Init;                   {Initialize the edit object}
  1154.    Memo_Store.Edit_Lgth := 50;        {Set default memo line size to 50}
  1155.    Wait_Cr := false;                  {Set EditString not to wait for CR}
  1156.    DeleteOnF9 := false;               {Turn off F9 for delete/undelete}
  1157. end;
  1158.  
  1159. function GS_dBFld_Objt.MemoGetLine(linenum : integer) : string;
  1160. begin
  1161.    if linenum > Memo_Store.Total_Lines then
  1162.    begin
  1163.       MemoGetLine := '';
  1164.       exit;
  1165.    end;
  1166.    if not Memo_Store.Find_Line(linenum) then
  1167.    begin
  1168.       MemoGetLine := '';
  1169.       exit;
  1170.    end;
  1171.    MemoGetLine := Memo_Store.Work_line^.Valu_Line;
  1172. end;
  1173.  
  1174. Procedure GS_dBFld_Objt.MemoGet(rpt : string);
  1175. const
  1176.    EOFMark : byte = $1A;              {End of disk file code}
  1177.  
  1178. var
  1179.    cnt,                               {Counter for memo storage location}
  1180.    lCnt,                              {Counter for line length in characters}
  1181.    mCnt    : longint;                 {Counter for input buffer char position}
  1182.    Result  : word;                    {BlockRead number of bytes read}
  1183.    done    : boolean;                 {Flag set when end of memo field found}
  1184.    i,j     : integer;                 {Working variable}
  1185.    Mem_Block : array [0..GS_dBase_MaxMemoRec] of byte;
  1186.                                       {Input buffer}
  1187. BEGIN                       { Get Memo Field }
  1188.    Val(rpt, Memo_Loc, i);             {Save starting block number}
  1189.    Memo_Bloks := 0;                   {Initialize blocks read}
  1190.    Memo_Store.Clear_Editor;           {Begin memo line count at zero}
  1191. {
  1192.                     ┌─────────────────────────────────────┐
  1193.                     │  If no .DBT memo field for this     │
  1194.                     │  record, then exit.                 │
  1195.                     └─────────────────────────────────────┘
  1196. }
  1197.    if (Memo_Loc = 0) then exit;
  1198.    Memo_Store.Work_Line := Memo_Store.Get_Line_Mem(Memo_Store.Edit_Lgth);
  1199.                                       {Get the first edit line record}
  1200.    Memo_Store.Active_Line := 1;       {Set active line to first line}
  1201.    done := false;                     {Reset done flag to false}
  1202.    cnt := 0;                          {index into Memo_Store buffer}
  1203.    lCnt := 0;                         {line length counter}
  1204.    BEGIN
  1205.       while (not done) do             {loop until done (EOF mark)}
  1206.       begin
  1207.          GS_FileRead(mFile, Memo_Loc+Memo_Bloks, Mem_Block, 1, Result);
  1208.          inc(Memo_Bloks);
  1209.          mCnt := 0;                   {Counter into disk read buffer}
  1210. {
  1211.                     ┌─────────────────────────────────────┐
  1212.                     │  Start reading and processing the   │
  1213.                     │  sequential memo blocks until EOF   │
  1214.                     │  mark is found.                     │
  1215.                     └─────────────────────────────────────┘
  1216. }
  1217.          while (mCnt < GS_dBase_MaxMemoRec) and
  1218.                (done = false) do
  1219. {
  1220.                  ┌────────────────────────────────────────────┐
  1221.                  │   Repeat the following until you find an   │
  1222.                  │   End-of-Memo condition.  Read the next    │
  1223.                  │   block each time mCnt reaches 512 bytes   │
  1224.                  │   (GS_dBase_MaxMemoRec.  Group the memo    │
  1225.                  │   as a series of lines no greater than     │
  1226.                  │   Memo_Width long.                         │
  1227.                  └────────────────────────────────────────────┘
  1228. }
  1229.          begin
  1230.  
  1231.             case Mem_Block[mCnt] of   {Check for control characters}
  1232.  
  1233.                $1A : begin
  1234.                         done := true; {End of Memo field}
  1235.                         if Memo_Store.Work_line^.Valu_Line = '' then
  1236.                            Memo_Store.Rel_Line_Mem(Memo_Store.Active_Line);
  1237.                      end;
  1238.  
  1239.                $8D : begin            {Soft Return (Wordstar and dBase editor)}
  1240.                         if (Memo_Store.Work_Line^.Valu_Line[lCnt] <> ' ') and
  1241.                            (Memo_Store.Work_Line^.Valu_Line[lCnt] <> '-') and
  1242.                            (lCnt > 0) then
  1243.                         begin
  1244.                            inc(lCnt); {Add to line length count}
  1245.                            Memo_Store.Work_Line^.Valu_Line[lcnt] := ' ';
  1246.                                       {Insert a space in storage}
  1247.                            Memo_Store.Work_Line^.Valu_Line[0] := chr(lcnt);
  1248.                         end;
  1249.                      end;
  1250.  
  1251.                $0A : begin            {Linefeed}
  1252.                      end;             {Ignore these characters}
  1253.  
  1254.                $0D : begin            {Hard Return}
  1255.                         With Memo_Store do
  1256.                         begin
  1257.                            Work_Line^.Return_Cod := $0D;
  1258.                            Work_Line := Get_Line_Mem(Edit_Lgth);
  1259.                            inc(Memo_Store.Active_Line);
  1260.                            lCnt := 0;
  1261.                         end;
  1262.                      end;
  1263.                else                   {Here for other characters}
  1264.                begin
  1265.                   inc(lCnt);          {Add to line length count}
  1266.                   Memo_Store.Work_Line^.Valu_Line[lcnt] :=
  1267.                                       chr(Mem_Block[mCnt]);
  1268.                                       {Insert the character in storage}
  1269.                   Memo_Store.Work_Line^.Valu_Line[0] := chr(lcnt);
  1270.                end;
  1271.             end;
  1272.             inc(mCnt);                {Step to next input buffer location}
  1273.  
  1274.             if lCnt > Memo_Store.Edit_Lgth then
  1275.                                       {If lcnt longer than Memo_Width, you}
  1276.                                       {must word wrap to Memo_Width length}
  1277.                                       {or less}
  1278.             begin
  1279.                while (Memo_Store.Work_Line^.Valu_Line[lCnt] <> ' ') and
  1280.                      (Memo_Store.Work_Line^.Valu_Line[lCnt] <> '-') and
  1281.                      (lCnt > 0) do dec(lCnt);
  1282.                                       {Repeat search for space or hyphen until}
  1283.                                       {found or current line exhausted}
  1284.                if (lCnt = 0) then
  1285.                   lcnt := length(Memo_Store.Work_Line^.Valu_Line) - 1;
  1286.                                       {If no break point, truncate line}
  1287.                with Memo_Store do
  1288.                begin
  1289.                   Temp_Line := Work_Line^.Valu_Line;
  1290.                   system.delete(Temp_Line,1,lCnt);
  1291.                   if lCnt > Memo_Store.Edit_Lgth then
  1292.                      lCnt := Memo_Store.Edit_Lgth;
  1293.                   Work_Line^.Valu_Line[0] := chr(lcnt);
  1294.                                       {Get string up to cursor to split line}
  1295.                   Work_Line := Get_Line_Mem(Edit_Lgth);
  1296.                   inc(Memo_Store.Active_Line);
  1297.                   Work_Line^.Return_Cod := $8D;
  1298.                                       {Insert soft return character}
  1299.                   Work_Line^.Valu_Line  := Temp_Line;
  1300.                   lCnt := length(Work_Line^.Valu_Line);
  1301.                end;
  1302.             end;
  1303.          end;
  1304.       END;
  1305.    end;
  1306. END;                        { Get Memo Field }
  1307.  
  1308. Procedure GS_dBFld_Objt.MemoEdit;
  1309. begin
  1310.    Memo_Store.Edit;
  1311. end;
  1312.  
  1313. Function GS_dBFld_Objt.MemoLines : integer;
  1314. begin
  1315.    MemoLines := Memo_Store.Total_Lines;
  1316. end;
  1317.  
  1318. Procedure GS_dBFld_Objt.MemoWidth(l : integer);
  1319. begin
  1320.    Memo_Store.Edit_Lgth := l;
  1321. end;
  1322.  
  1323. Function GS_dBFld_Objt.MemoPut : string;
  1324. const
  1325.    EOFMark : byte = $1A;              {End of disk file code}
  1326. var
  1327.    bCnt,                              {Will hold bytes in memo field}
  1328.    lCnt,                              {Counter for line length in characters}
  1329.    mCnt,
  1330.    tcnt  :  longint;                  {Counter for input buffer char position}
  1331.    Result  : word;                    {BlockWrite number of bytes written}
  1332.    i     : longint;                   {Working variable}
  1333.    Mem_Block : array [0..GS_dBase_MaxMemoRec*2] of byte;
  1334.                                       {Output buffer}
  1335.    valu  : string[10];                {work string to convert block number}
  1336. BEGIN                       { Put Memo Field }
  1337.    bCnt := Memo_Store.Byte_Count;     {Get count of bytes in memo field}
  1338.    bCnt := bcnt div GS_dBase_MaxMemoRec;
  1339.                                       {Get number of blocks required}
  1340.    inc(bCnt);                         {Adjust from zero}
  1341.    if bCnt > Memo_Bloks then
  1342.    begin
  1343.       GS_FileRead(mFile, 0, Mem_Block, 1, Result);
  1344.                                       {read a block from the .DBT}
  1345.       Move(Mem_Block[0],Memo_Loc,4);
  1346.                                       {Get next block number to append}
  1347.    end;
  1348.    Memo_Bloks := bCnt;                {Set blocks written count}
  1349.    lCnt := 0;                         {line length counter}
  1350.    mCnt := 0;                         {Counter into disk write buffer}
  1351.    tCnt := Memo_Loc;
  1352. {
  1353.                     ┌─────────────────────────────────────┐
  1354.                     │  Start reading and processing the   │
  1355.                     │  sequential memo blocks until EOF   │
  1356.                     │  mark is found.                     │
  1357.                     └─────────────────────────────────────┘
  1358. }
  1359.       with Memo_Store do
  1360.       begin
  1361.          Work_Line := First_Line;
  1362.          while (Work_Line <> nil) do
  1363.          begin
  1364.             move(Work_Line^.Valu_Line[1],Mem_Block[mCnt],
  1365.                  length(Work_Line^.Valu_Line));
  1366.             mCnt := mCnt + length(Work_Line^.Valu_Line);
  1367.             if Work_Line^.Next_Line <> nil then
  1368.             begin
  1369.                Mem_Block[mCnt] := Work_Line^.Return_Cod;
  1370.                Mem_Block[mCnt+1] := $0A;
  1371.                inc(mCnt,2);
  1372.             end;
  1373.             Work_Line := Work_Line^.Next_Line;
  1374.             if (mCnt > GS_dBase_MaxMemoRec) then
  1375.             begin
  1376.                GS_FileWrite(mFile,tcnt,Mem_Block,1, Result);
  1377.                                       {read a block from the .DBT}
  1378.                inc(tcnt);
  1379.                mCnt := mCnt mod GS_dBase_MaxMemoRec;
  1380.                                       {Get excess buffer length used}
  1381.                Move(Mem_Block[GS_dBase_MaxMemoRec],Mem_Block[0],mCnt);
  1382.                                       {Move excess to beginning of buffer}
  1383.             end;
  1384.          end;
  1385.          Mem_Block[mCnt] := EOFMark;
  1386.          FillChar(Mem_Block[succ(mcnt)],GS_dBase_MaxMemoRec - mcnt,#0);
  1387.          GS_FileWrite(mFile,tcnt,Mem_Block,1, Result);
  1388.                                       {Write the last block to the .DBT}
  1389.          i := GS_FileSize(mFile);
  1390.          FillChar(Mem_Block,GS_dBase_MaxMemoRec,#0);
  1391.          Move(i,Mem_Block[0],4);
  1392.          GS_FileWrite(mFile,0,Mem_Block,1, Result);
  1393.                                       {Write the block to the .DBT.  It will}
  1394.                                       {point to the next available block};
  1395.    end;
  1396.    Str(Memo_Loc:10,valu);
  1397.    MemoPut := valu;
  1398. end;
  1399.  
  1400. end.
  1401.  
  1402.  
  1403.